package com.jasperframework.boot.file;

import org.apache.commons.lang3.RandomStringUtils;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>
 *  PATH生成器 <br>
 *  22位32进制编码，依次为：0-9,A-V
 *  0:类别编号
 *  1-3:服务器编号
 *  4-5:启动编号
 *  6-12:时间戳，秒级
 *  13-19:序列，循环递增
 *  20-21:校验码
 *  </p>
 * @site https://miliqkdoc.motopa.cn/
 * @author daizhao
 * @date 2017/11/29 13:44
 */
public class PathGen {

	/**
	 * 编码字典
	 */
	private final static char[] ENC = "0123456789ABCDEFGHIJKLMNOPQRSTUV".toCharArray();

	private final static char[] UP_LETTER = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};


	/**
	 * 默认实例
	 */
	public static final PathGen DEFAULT = new PathGen("D", new DecimalFormat("000").format(Math.random() * 1000));

	/**
	 * 类别编号
	 */
	private final char[] type;

	/**
	 * 服务器编号
	 */
	private final char[] server;

	/**
	 * 启动编号
	 */
	private final char[] startup;

	/**
	 * 序列号
	 */
	private final AtomicInteger seq = new AtomicInteger(0);


	public static String getPath(String originName) {
		String extension = handleSuffix(originName);
		String fileName = PathGen.DEFAULT.gen() + extension;
		int hash = fileName.hashCode();
		StringBuilder sb = new StringBuilder(File.separator + "fmsf");
		sb.append("/").append(ENC[hash >>> 29 & 0x7]).append(ENC[hash >>> 24 & 0x1f]);
		sb.append("/").append(ENC[hash >>> 21 & 0x7]).append(ENC[hash >>> 16 & 0x1f]);
		sb.append("/").append(ENC[hash >>> 13 & 0x7]).append(ENC[hash >>> 8 & 0x1f]);
		sb.append("/").append(ENC[hash >>> 5 & 0x7]).append(ENC[hash & 0x1f]);
		sb.append("/").append(fileName);
		return sb.toString();
	}

	/**
	 * <p> 生成文件路径</p>
	 * @param originName	原文件名
	 * @param folder		子目录
	 * @param useOriginNameAsPrefix	是否使用原文件名作为前缀
	 * @date:  10:30 2024/3/21 0021
	 * @return: java.lang.String
	 * @author: daizhao
	 */
	public static String getPath(String originName, String folder, boolean useOriginNameAsPrefix) {
		String extension = handleSuffix(originName);
		StringBuilder sb = new StringBuilder();
		StringBuilder filename = new StringBuilder();
		if(folder.contains("uploadNovelTask") || useOriginNameAsPrefix) {
			filename.append(originName, 0, originName.lastIndexOf('.')).append("_").append(generateRandomFilename()).append(extension);
		}else{
			filename.append(generateRandomFilename()).append(extension);
		}
		if(folder.contains("#")){
			sb.append(File.separator + "fmsf" + File.separator);
			Arrays.stream(folder.split("#")).forEach(f-> sb.append(f + File.separator));
		}else{
			sb.append(File.separator + "fmsf" + File.separator + folder + File.separator);
		}
		sb.append(filename);
		return sb.toString();
	}

	/**
	 * 处理扩展名
	 * @param originName 原文件名
	 * @return
	 */
	private static String handleSuffix(String originName){
		StringBuilder extension = new StringBuilder();
		if (Objects.isNull(originName)) {
			return "";
		}
		//文件后缀处理
		if(originName.indexOf('.') > 0){
			extension.append(originName.substring(originName.lastIndexOf('.')));
		}else{
			extension.append(".txt");
		}
		return extension.toString();
	}

	/**
	 * 创建ID生成器实例
	 *
	 * @param type
	 *            类别编号，1位，^([0-9]|[A-V]){1}$
	 * @param server
	 *            服务编号，3位，^([0-9]|[A-V]){3}$
	 */
	private PathGen(String type, String server) {
		if (type == null) {
			throw new NullPointerException("type");
		}
		if (server == null) {
			throw new NullPointerException("server");
		}
		if (check(type, 1) == false) {
			throw new IllegalArgumentException("type must be \"^([0-9]|[A-V]){1}$\"");
		}
		if (check(server, 3) == false) {
			throw new IllegalArgumentException("server must be \"^([0-9]|[A-V]){3}$\"");
		}
		this.type = type.toCharArray();
		this.server = server.toCharArray();
		int s;
		try {
			// 首选进程号
			s = Math.abs(Integer.parseInt(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]) % 1024);
		} catch (Exception e) {
			// 进程号失败,使用随机数
			s = new SecureRandom().nextInt(1024);
		}
		this.startup = new char[2];
		this.startup[0] = ENC[s >>> 5];
		this.startup[1] = ENC[s & 0x1f];
	}

	/**
	 * 生成ID
	 *
	 * @return ID
	 */
	private String gen() {
		// 时间为秒，正最大可支持到2038年，之后变为负数，可支持到2106年
		return gen((int) (System.currentTimeMillis() / 1000L), seq.getAndIncrement());
	}

	/**
	 * 生成ID
	 */
	private String gen(final int time, final int seq) {
		char[] cs = new char[22];
		cs[0] = type[0];
		cs[1] = server[0];
		cs[2] = server[1];
		cs[3] = server[2];
		cs[4] = startup[0];
		cs[5] = startup[1];
		cs[6] = ENC[time >>> 30 & 0x1f];
		cs[7] = ENC[time >>> 25 & 0x1f];
		cs[8] = ENC[time >>> 20 & 0x1f];
		cs[9] = ENC[time >>> 15 & 0x1f];
		cs[10] = ENC[time >>> 10 & 0x1f];
		cs[11] = ENC[time >>> 5 & 0x1f];
		cs[12] = ENC[time & 0x1f];
		cs[13] = ENC[seq >>> 30 & 0x1f];
		cs[14] = ENC[seq >>> 25 & 0x1f];
		cs[15] = ENC[seq >>> 20 & 0x1f];
		cs[16] = ENC[seq >>> 15 & 0x1f];
		cs[17] = ENC[seq >>> 10 & 0x1f];
		cs[18] = ENC[seq >>> 5 & 0x1f];
		cs[19] = ENC[seq & 0x1f];
		cs[20] = ENC[(cs[0] + cs[2] * 2 + cs[4] * 3 + cs[6] * 4 + cs[8] * 5 + cs[10] * 6 + cs[12] * 7 + cs[14] * 8
				+ cs[17] * 9 + cs[19] * 10) & 0x1f];
		cs[21] = ENC[(cs[1] + cs[3] * 2 + cs[5] * 3 + cs[7] * 4 + cs[9] * 5 + cs[11] * 6 + cs[13] * 7 + cs[15] * 8
				+ cs[16] * 9 + cs[18] * 10) & 0x1f];
		return new String(cs);
	}

	/**
	 * 合法性检查
	 */
	private boolean check(String str, int len) {
		if (str.length() != len) {
			return false;
		}
		for (int i = 0; i < len; i++) {
			char c = str.charAt(i);
			if (!((c >= 'A' && c <= 'V') || (c >= '0' && c <= '9'))) {
				return false;
			}
		}
		return true;
	}

	//生成随机文件名，防止上传文件后文件名重复
	public static String generateRandomFilename(){
		Calendar calCurrent = Calendar.getInstance();
		int day = calCurrent.get(Calendar.DATE);
		int month = calCurrent.get(Calendar.MONTH) + 1;
		int year = calCurrent.get(Calendar.YEAR);
		long millisecond = calCurrent.getTimeInMillis();
		StringBuffer now = new StringBuffer();
		now.append(RandomStringUtils.random(1, UP_LETTER))
				.append(year).append(month).append(day)
				.append(millisecond)
				.append(RandomStringUtils.random(6, UP_LETTER))
				.append(RandomStringUtils.random(6, "0123456789"));
		return now.toString();
	}

	public static void main(String[] args) {
		String originName = "str.txt";
		String extension = handleSuffix(originName);
		StringBuilder filename = new StringBuilder();
		filename.append(originName, 0, originName.lastIndexOf('.')).append("-").append(generateRandomFilename()).append(extension);
		System.out.println("1：" + getPath("str"));
		System.out.println("2：" + filename);
		String path = getPath(originName, "font", true);
		System.out.println("3：" + path);
        System.out.println("originName.substring(0, originName.lastIndexOf('.')) = " + originName.substring(0, originName.lastIndexOf('.')));
	}
}
